#ifndef codegenerator_h__
#define codegenerator_h__

#include <fstream>
#include <string>
#include "symbolstable.h"
#include "asmcmd.h"
#include <vector>
#include <stack>

namespace 	CCompiler
{
	class Generator
	{
		std::ofstream outfile_;
		SymTable &symtable_;
        SymTableStack stack;
        std::vector<Asm::Cmd *> code_;
        size_t literalCounter;
        size_t labelCounter;
        std::vector<std::pair<std::string, std::string> > strLiterals;
        std::stack<std::pair<std::string, std::string> > loops;

        struct FunctionHandler  
        {
            size_t returnOffset;
            SymFunction *currentFunction;
            size_t deepestBlockBegin;
        } functionHandler;

	public:
		Generator(const std::string &outfileName, SymTable &symtable):
		  outfile_(outfileName.c_str()), symtable_(symtable), stack(&symtable_), literalCounter(0), labelCounter(0) {}
        std::string mangling(std::string name, SymbolType type);
        SymTableStack &accessTable() { return stack; }
        std::string getStrLiteralName();
        void addStrLiteral(std::string name, std::string str);
		void generateHeader();
		void generateFooter();
        void genDb(std::string name, size_t size);
        void genLabel(std::string name);
        std::string getLabel();
        void genOp(std::string mnem);
        size_t getReturnOffset() { return functionHandler.returnOffset; }
        size_t getDeepestBlockBegin() { return functionHandler.deepestBlockBegin; }
        void setDeepestBlockBegin(size_t dbb) { functionHandler.deepestBlockBegin = dbb; }
        SymFunction *getCurrentFunctuin() { return functionHandler.currentFunction; }
        std::string getLoopBegin() { return loops.top().first; }
        std::string getLoopEnd() { return loops.top().second; }
        void popLoop() { loops.pop(); }
        void pushLoop(std::string begin, std::string end) { loops.push(std::make_pair(begin, end)); }
        void pushTable(SymTableInterface *tbl) { stack.push(tbl); }
        void popTable() { stack.pop(); }
        template<class T1>
        void genOp(std::string mnem, T1 op1)
        {
            code_.push_back(&(new Asm::Op(mnem))->add(new T1(op1)));
        }
        template<class T1, class T2>
        void genOp(std::string mnem, T1 op1, T2 op2)
        {
            code_.push_back(&(new Asm::Op(mnem))->add(new T1(op1)).add(new T2(op2)));
        }
		void generate();
        ~Generator();
	};
} 
namespace __InitializeCodegenerator
{
    void initialize();
}

#endif // codegenerator_h__
